package com.societyGames.robotsExercises
{
import com.societyGames.action.ActionQueue;

import flash.geom.Point;
import flash.utils.Dictionary;

public class RobotsSimulation
{
  public var showNumbers:Boolean;

  [TypeHint(type="com.societyGames.robotsExercises.RobotData")]
  public var robotDatas:Array;
  public var homeX:int = int.MIN_VALUE;
  public var homeY:int = int.MIN_VALUE;
  public var spaceText:String; //A text representation of blocked spots. O means blocked. T means target.

  public const gridWidth:int = 5;
  public const gridHeight:int = 5;

  private const _wallCharacter:String = "0";
  private const _targetCharacter:String = "T";
  private var _render:RobotsRender;
  private var _homePosition:Point;
  private var _nameToRobotPosition:Dictionary = new Dictionary(); //name:String -> position:Point
  private var _spaces:Vector.<Vector.<Space>>;

  public function initialize(actionQueue:ActionQueue):void
  {
    createSpaces();
    this._render = new RobotsRender(this, actionQueue);

    placeHome();
    placeItems();

    if (this.showNumbers)
    {
      this._render.showNumbers();
    }

    for each (var robotData:RobotData in robotDatas)
    {
      createRobot(robotData);
    }
    //placeRobotExplicit(this.robotX, this.robotY);
  }

  private function createSpaces():void
  {
    _spaces = new Vector.<Vector.<Space>>();
    for (var x:int = 0; x < gridWidth; x++)
    {
      var column:Vector.<Space> = new Vector.<Space>();
      _spaces.push(column);
      for (var y:int = 0; y < gridHeight; y++)
      {
        column.push(new Space(Space.NORMAL));
      }
    }
  }

  private function placeHome():void
  {
    if (this.homeX == int.MIN_VALUE || this.homeY == int.MIN_VALUE)
    {
      _homePosition = new Point(Math.floor(gridWidth / 2), Math.floor(gridHeight / 2));
    }
    else
    {
      _homePosition = new Point(homeX, homeY);
    }
    this._spaces[_homePosition.x][_homePosition.y].type = Space.HOME;
    this._render.setHome(this._homePosition);
  }

  private function placeItems():void
  {
    if (spaceText != null)
    {
      if (spaceText.length > gridWidth * gridHeight)
      {
        throw new Error("blockText is invalid.");
      }

      var index:int = 0;
      for (var y:int = 0; y < gridHeight; y++)
      {
        for (var x:int = 0; x < gridWidth; x++)
        {
          var character:String = spaceText.substr(index, 1);
          switch (character)
          {
            case _wallCharacter:
              this._spaces[x][y].type = Space.WALL;
              this._render.placeWall(x, y);
              break;
            case _targetCharacter:
              this._spaces[x][y].type = Space.WALL;
              this._render.placeButton(x, y);
              break;
          }
          index++;
        }
      }
    }
  }

  public function get render():RobotsRender
  {
    return this._render;
  }

  public function createRobot(robotData:RobotData):void
  {
    var position:Point = new Point(robotData.x, robotData.y);
    this._nameToRobotPosition[robotData.name] = position;
    this._spaces[position.x][position.y].hasVisited = true;
    this._render.createRobot(robotData.name, position);
  }

  public function setHomePosition(homeX:int, homeY:int):void
  {
    this._homePosition = new Point(homeX, homeY);
    this._render.setHome(this._homePosition);
  }

  public function areRobotsHome():Boolean
  {
    for each (var point:Point in _nameToRobotPosition)
    {
      if (!point.equals(_homePosition))
      {
        return false;
      }
    }
    return true;
  }

  public function move(name:String, dx:int, dy:int):void
  {
    var robotPosition:Point = _nameToRobotPosition[name];
    moveTo(name, robotPosition.x + dx, robotPosition.y + dy);
  }

  public function moveTo(name:String, x:int, y:int):void
  {
    var robotPosition:Point = _nameToRobotPosition[name];
    robotPosition.x = x;
    robotPosition.y = y;
    this._render.moveRobot(name, robotPosition.x, robotPosition.y);
  }

  public function getRobotPosition(name:String):Point
  {
    return _nameToRobotPosition[name];
  }

  private function moveDiscrete(name:String, dx:int, dy:int):void
  {
    var robotPosition:Point = _nameToRobotPosition[name];
    var endX:int = robotPosition.x + dx;
    var endY:int = robotPosition.y + dy;
    if (isBlocked(endX, endY))
    {
      this._render.moveIllegal(name,
              robotPosition.x,
              robotPosition.y,
              endX,
              endY
      );
    }
    else
    {
      _spaces[endX][endY].hasVisited = true;
      move(name, dx, dy);
    }
  }

  public function isBlocked(x:int, y:int):Boolean
  {
    return x < 0 || x >= gridWidth || y < 0 || y >= gridHeight || this._spaces[x][y].type == Space.WALL;
  }

  public function hasVisited(x:int, y:int):Boolean
  {
    return x >= 0 && x < this.gridWidth &&
            y >= 0 && y < this.gridHeight &&
            _spaces[x][y].hasVisited;
  }
}
}
